Skip to content

Mock 框架的使用

单元测试中 Mock 的用法和写法,基本的内容可以问一下大模型。

基于 JUnit5,使用 MockitoExtension 和 @Mock,用于写单元测试。

  • 注解
    • Mock
      • 用于创建某个类(或接口)的模拟对象(Mock Object)
    • InjectMocks
      • 用于标记被测试对象(即你想要测试的类)
  • 单测
    • 调用 mock 方法时一定要提前用 when(...).thenReturn(...) 或 doReturn(...) 等设置好期望行为,否则 mock 返回的都是 null 或默认值,容易导致 NullPointerException。

一般都是先 mock,然后调用方法

@Tag("serviceTest")  
@ExtendWith(MockitoExtension.class)  
public class SelfActivityAppServiceNewTest {  
  
    // === Core dependencies ===  
    @Mock  
    private SelfActivityService selfActivityService;
    

}

如果使用大模型去写单测的时候,有时候建议还是手动微调一下, 有时候可能就是mock参数的问题(当然也有可能是提示词的问题),如果让大模型写,效果比预期可能差一些。


项目框架

SpringBoot,覆盖率使用的是 jacoco ,依赖管理使用的是 maven

单元测试写法

基本的一些用法

常用注解

注解用途
@Mock创建模拟对象,所有方法默认返回空值
@InjectMocks创建真实对象,并注入所有@Mock对象作为依赖
@BeforeEach每个测试方法执行前运行,用于初始化
@SpringBootTest加载完整应用上下文的集成测试
@WebMvcTest仅测试MVC控制器,加载相关Web层组件
@DataJpaTest专门测试JPA组件

Mockito 基本用法

java
// 设置模拟行为
when(userRepository.findById(1L)).thenReturn(Optional.of(new User(1L, "张三")));

// 模拟任意参数
when(service.process(any())).thenReturn(result);

// 初始化Mocks (通常在@BeforeEach中)
MockitoAnnotations.openMocks(this);

常用断言

java
// 非空检查
assertNotNull(result);

// 相等性检查
assertEquals(expected, actual);
assertEquals(3.14, value, 0.01); // 带容差的浮点数比较

// 布尔条件检查
assertTrue(condition);
assertFalse(list.isEmpty());

// 异常检查
assertThrows(IllegalArgumentException.class, () -> service.method(invalidArg));

验证方法调用

java
// 验证方法被调用一次
verify(service).method(arg);

// 验证调用次数
verify(service, times(2)).method(any());
verify(service, never()).unusedMethod();
verify(service, atLeastOnce()).method();

// 验证无其他调用发生
verifyNoMoreInteractions(service, repository);

// 验证调用顺序
InOrder order = inOrder(service, repo);
order.verify(service).first();
order.verify(repo).second();

测试模板

java
@ExtendWith(MockitoExtension.class)
class ServiceTest {
    @Mock
    private Repository repository;
    
    @InjectMocks
    private ServiceImpl service;
    
    @BeforeEach
    void setUp() {
        // 初始化工作,设置通用mock行为
        when(repository.findById(1L)).thenReturn(Optional.of(new Entity(1L)));
    }
    
    @Test
    void testMethod_Scenario_ExpectedResult() {
        // 准备
        when(repository.specificMethod()).thenReturn(expectedValue);
        
        // 执行
        Result result = service.methodUnderTest(input);
        
        // 验证
        assertNotNull(result);
        assertEquals(expected, result.getValue());
        assertTrue(result.isValid());
        
        // 行为验证
        verify(repository).specificMethod();
        verifyNoMoreInteractions(repository);
    }
}

最佳实践

  1. 单一职责:每个测试方法只测一个功能点
  2. 准备-执行-验证:遵循AAA模式(Arrange-Act-Assert)
  3. 测试边界条件:包括正常情况和异常情况
  4. 隔离依赖:使用@Mock隔离外部依赖
  5. 可读性优先:使用描述性命名和适当注释
  6. 断言在先,验证在后:先验证结果正确,再验证行为正确